1 /* 2 * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javax.swing.table; 27 28 import java.io.Serializable; 29 import java.util.Vector; 30 import java.util.Enumeration; 31 import javax.swing.event.TableModelEvent; 32 33 34 /** 35 * This is an implementation of <code>TableModel</code> that 36 * uses a <code>Vector</code> of <code>Vectors</code> to store the 37 * cell value objects. 38 * <p> 39 * <strong>Warning:</strong> <code>DefaultTableModel</code> returns a 40 * column class of <code>Object</code>. When 41 * <code>DefaultTableModel</code> is used with a 42 * <code>TableRowSorter</code> this will result in extensive use of 43 * <code>toString</code>, which for non-<code>String</code> data types 44 * is expensive. If you use <code>DefaultTableModel</code> with a 45 * <code>TableRowSorter</code> you are strongly encouraged to override 46 * <code>getColumnClass</code> to return the appropriate type. 47 * <p> 48 * <strong>Warning:</strong> 49 * Serialized objects of this class will not be compatible with 50 * future Swing releases. The current serialization support is 51 * appropriate for short term storage or RMI between applications running 52 * the same version of Swing. As of 1.4, support for long term storage 53 * of all JavaBeans<sup><font size="-2">TM</font></sup> 54 * has been added to the <code>java.beans</code> package. 55 * Please see {@link java.beans.XMLEncoder}. 56 * 57 * @author Philip Milne 58 * 59 * @see TableModel 60 * @see #getDataVector 61 */ 62 public class DefaultTableModel extends AbstractTableModel implements Serializable { 63 64 // 65 // Instance Variables 66 // 67 68 /** 69 * The <code>Vector</code> of <code>Vectors</code> of 70 * <code>Object</code> values. 71 */ 72 protected Vector dataVector; 73 74 /** The <code>Vector</code> of column identifiers. */ 75 protected Vector columnIdentifiers; 76 77 // 78 // Constructors 79 // 80 81 /** 82 * Constructs a default <code>DefaultTableModel</code> 83 * which is a table of zero columns and zero rows. 84 */ 85 public DefaultTableModel() { 86 this(0, 0); 87 } 88 89 private static Vector newVector(int size) { 90 Vector v = new Vector(size); 91 v.setSize(size); 92 return v; 93 } 94 95 /** 96 * Constructs a <code>DefaultTableModel</code> with 97 * <code>rowCount</code> and <code>columnCount</code> of 98 * <code>null</code> object values. 99 * 100 * @param rowCount the number of rows the table holds 101 * @param columnCount the number of columns the table holds 102 * 103 * @see #setValueAt 104 */ 105 public DefaultTableModel(int rowCount, int columnCount) { 106 this(newVector(columnCount), rowCount); 107 } 108 109 /** 110 * Constructs a <code>DefaultTableModel</code> with as many columns 111 * as there are elements in <code>columnNames</code> 112 * and <code>rowCount</code> of <code>null</code> 113 * object values. Each column's name will be taken from 114 * the <code>columnNames</code> vector. 115 * 116 * @param columnNames <code>vector</code> containing the names 117 * of the new columns; if this is 118 * <code>null</code> then the model has no columns 119 * @param rowCount the number of rows the table holds 120 * @see #setDataVector 121 * @see #setValueAt 122 */ 123 public DefaultTableModel(Vector columnNames, int rowCount) { 124 setDataVector(newVector(rowCount), columnNames); 125 } 126 127 /** 128 * Constructs a <code>DefaultTableModel</code> with as many 129 * columns as there are elements in <code>columnNames</code> 130 * and <code>rowCount</code> of <code>null</code> 131 * object values. Each column's name will be taken from 132 * the <code>columnNames</code> array. 133 * 134 * @param columnNames <code>array</code> containing the names 135 * of the new columns; if this is 136 * <code>null</code> then the model has no columns 137 * @param rowCount the number of rows the table holds 138 * @see #setDataVector 139 * @see #setValueAt 140 */ 141 public DefaultTableModel(Object[] columnNames, int rowCount) { 142 this(convertToVector(columnNames), rowCount); 143 } 144 145 /** 146 * Constructs a <code>DefaultTableModel</code> and initializes the table 147 * by passing <code>data</code> and <code>columnNames</code> 148 * to the <code>setDataVector</code> method. 149 * 150 * @param data the data of the table, a <code>Vector</code> 151 * of <code>Vector</code>s of <code>Object</code> 152 * values 153 * @param columnNames <code>vector</code> containing the names 154 * of the new columns 155 * @see #getDataVector 156 * @see #setDataVector 157 */ 158 public DefaultTableModel(Vector data, Vector columnNames) { 159 setDataVector(data, columnNames); 160 } 161 162 /** 163 * Constructs a <code>DefaultTableModel</code> and initializes the table 164 * by passing <code>data</code> and <code>columnNames</code> 165 * to the <code>setDataVector</code> 166 * method. The first index in the <code>Object[][]</code> array is 167 * the row index and the second is the column index. 168 * 169 * @param data the data of the table 170 * @param columnNames the names of the columns 171 * @see #getDataVector 172 * @see #setDataVector 173 */ 174 public DefaultTableModel(Object[][] data, Object[] columnNames) { 175 setDataVector(data, columnNames); 176 } 177 178 /** 179 * Returns the <code>Vector</code> of <code>Vectors</code> 180 * that contains the table's 181 * data values. The vectors contained in the outer vector are 182 * each a single row of values. In other words, to get to the cell 183 * at row 1, column 5: <p> 184 * 185 * <code>((Vector)getDataVector().elementAt(1)).elementAt(5);</code><p> 186 * 187 * @return the vector of vectors containing the tables data values 188 * 189 * @see #newDataAvailable 190 * @see #newRowsAdded 191 * @see #setDataVector 192 */ 193 public Vector getDataVector() { 194 return dataVector; 195 } 196 197 private static Vector nonNullVector(Vector v) { 198 return (v != null) ? v : new Vector(); 199 } 200 201 /** 202 * Replaces the current <code>dataVector</code> instance variable 203 * with the new <code>Vector</code> of rows, <code>dataVector</code>. 204 * Each row is represented in <code>dataVector</code> as a 205 * <code>Vector</code> of <code>Object</code> values. 206 * <code>columnIdentifiers</code> are the names of the new 207 * columns. The first name in <code>columnIdentifiers</code> is 208 * mapped to column 0 in <code>dataVector</code>. Each row in 209 * <code>dataVector</code> is adjusted to match the number of 210 * columns in <code>columnIdentifiers</code> 211 * either by truncating the <code>Vector</code> if it is too long, 212 * or adding <code>null</code> values if it is too short. 213 * <p>Note that passing in a <code>null</code> value for 214 * <code>dataVector</code> results in unspecified behavior, 215 * an possibly an exception. 216 * 217 * @param dataVector the new data vector 218 * @param columnIdentifiers the names of the columns 219 * @see #getDataVector 220 */ 221 public void setDataVector(Vector dataVector, Vector columnIdentifiers) { 222 this.dataVector = nonNullVector(dataVector); 223 this.columnIdentifiers = nonNullVector(columnIdentifiers); 224 justifyRows(0, getRowCount()); 225 fireTableStructureChanged(); 226 } 227 228 /** 229 * Replaces the value in the <code>dataVector</code> instance 230 * variable with the values in the array <code>dataVector</code>. 231 * The first index in the <code>Object[][]</code> 232 * array is the row index and the second is the column index. 233 * <code>columnIdentifiers</code> are the names of the new columns. 234 * 235 * @param dataVector the new data vector 236 * @param columnIdentifiers the names of the columns 237 * @see #setDataVector(Vector, Vector) 238 */ 239 public void setDataVector(Object[][] dataVector, Object[] columnIdentifiers) { 240 setDataVector(convertToVector(dataVector), convertToVector(columnIdentifiers)); 241 } 242 243 /** 244 * Equivalent to <code>fireTableChanged</code>. 245 * 246 * @param event the change event 247 * 248 */ 249 public void newDataAvailable(TableModelEvent event) { 250 fireTableChanged(event); 251 } 252 253 // 254 // Manipulating rows 255 // 256 257 private void justifyRows(int from, int to) { 258 // Sometimes the DefaultTableModel is subclassed 259 // instead of the AbstractTableModel by mistake. 260 // Set the number of rows for the case when getRowCount 261 // is overridden. 262 dataVector.setSize(getRowCount()); 263 264 for (int i = from; i < to; i++) { 265 if (dataVector.elementAt(i) == null) { 266 dataVector.setElementAt(new Vector(), i); 267 } 268 ((Vector)dataVector.elementAt(i)).setSize(getColumnCount()); 269 } 270 } 271 272 /** 273 * Ensures that the new rows have the correct number of columns. 274 * This is accomplished by using the <code>setSize</code> method in 275 * <code>Vector</code> which truncates vectors 276 * which are too long, and appends <code>null</code>s if they 277 * are too short. 278 * This method also sends out a <code>tableChanged</code> 279 * notification message to all the listeners. 280 * 281 * @param e this <code>TableModelEvent</code> describes 282 * where the rows were added. 283 * If <code>null</code> it assumes 284 * all the rows were newly added 285 * @see #getDataVector 286 */ 287 public void newRowsAdded(TableModelEvent e) { 288 justifyRows(e.getFirstRow(), e.getLastRow() + 1); 289 fireTableChanged(e); 290 } 291 292 /** 293 * Equivalent to <code>fireTableChanged</code>. 294 * 295 * @param event the change event 296 * 297 */ 298 public void rowsRemoved(TableModelEvent event) { 299 fireTableChanged(event); 300 } 301 302 /** 303 * Obsolete as of Java 2 platform v1.3. Please use <code>setRowCount</code> instead. 304 */ 305 /* 306 * Sets the number of rows in the model. If the new size is greater 307 * than the current size, new rows are added to the end of the model 308 * If the new size is less than the current size, all 309 * rows at index <code>rowCount</code> and greater are discarded. <p> 310 * 311 * @param rowCount the new number of rows 312 * @see #setRowCount 313 */ 314 public void setNumRows(int rowCount) { 315 int old = getRowCount(); 316 if (old == rowCount) { 317 return; 318 } 319 dataVector.setSize(rowCount); 320 if (rowCount <= old) { 321 fireTableRowsDeleted(rowCount, old-1); 322 } 323 else { 324 justifyRows(old, rowCount); 325 fireTableRowsInserted(old, rowCount-1); 326 } 327 } 328 329 /** 330 * Sets the number of rows in the model. If the new size is greater 331 * than the current size, new rows are added to the end of the model 332 * If the new size is less than the current size, all 333 * rows at index <code>rowCount</code> and greater are discarded. <p> 334 * 335 * @see #setColumnCount 336 * @since 1.3 337 */ 338 public void setRowCount(int rowCount) { 339 setNumRows(rowCount); 340 } 341 342 /** 343 * Adds a row to the end of the model. The new row will contain 344 * <code>null</code> values unless <code>rowData</code> is specified. 345 * Notification of the row being added will be generated. 346 * 347 * @param rowData optional data of the row being added 348 */ 349 public void addRow(Vector rowData) { 350 insertRow(getRowCount(), rowData); 351 } 352 353 /** 354 * Adds a row to the end of the model. The new row will contain 355 * <code>null</code> values unless <code>rowData</code> is specified. 356 * Notification of the row being added will be generated. 357 * 358 * @param rowData optional data of the row being added 359 */ 360 public void addRow(Object[] rowData) { 361 addRow(convertToVector(rowData)); 362 } 363 364 /** 365 * Inserts a row at <code>row</code> in the model. The new row 366 * will contain <code>null</code> values unless <code>rowData</code> 367 * is specified. Notification of the row being added will be generated. 368 * 369 * @param row the row index of the row to be inserted 370 * @param rowData optional data of the row being added 371 * @exception ArrayIndexOutOfBoundsException if the row was invalid 372 */ 373 public void insertRow(int row, Vector rowData) { 374 dataVector.insertElementAt(rowData, row); 375 justifyRows(row, row+1); 376 fireTableRowsInserted(row, row); 377 } 378 379 /** 380 * Inserts a row at <code>row</code> in the model. The new row 381 * will contain <code>null</code> values unless <code>rowData</code> 382 * is specified. Notification of the row being added will be generated. 383 * 384 * @param row the row index of the row to be inserted 385 * @param rowData optional data of the row being added 386 * @exception ArrayIndexOutOfBoundsException if the row was invalid 387 */ 388 public void insertRow(int row, Object[] rowData) { 389 insertRow(row, convertToVector(rowData)); 390 } 391 392 private static int gcd(int i, int j) { 393 return (j == 0) ? i : gcd(j, i%j); 394 } 395 396 private static void rotate(Vector v, int a, int b, int shift) { 397 int size = b - a; 398 int r = size - shift; 399 int g = gcd(size, r); 400 for(int i = 0; i < g; i++) { 401 int to = i; 402 Object tmp = v.elementAt(a + to); 403 for(int from = (to + r) % size; from != i; from = (to + r) % size) { 404 v.setElementAt(v.elementAt(a + from), a + to); 405 to = from; 406 } 407 v.setElementAt(tmp, a + to); 408 } 409 } 410 411 /** 412 * Moves one or more rows from the inclusive range <code>start</code> to 413 * <code>end</code> to the <code>to</code> position in the model. 414 * After the move, the row that was at index <code>start</code> 415 * will be at index <code>to</code>. 416 * This method will send a <code>tableChanged</code> notification 417 * message to all the listeners. <p> 418 * 419 * <pre> 420 * Examples of moves: 421 * <p> 422 * 1. moveRow(1,3,5); 423 * a|B|C|D|e|f|g|h|i|j|k - before 424 * a|e|f|g|h|B|C|D|i|j|k - after 425 * <p> 426 * 2. moveRow(6,7,1); 427 * a|b|c|d|e|f|G|H|i|j|k - before 428 * a|G|H|b|c|d|e|f|i|j|k - after 429 * <p> 430 * </pre> 431 * 432 * @param start the starting row index to be moved 433 * @param end the ending row index to be moved 434 * @param to the destination of the rows to be moved 435 * @exception ArrayIndexOutOfBoundsException if any of the elements 436 * would be moved out of the table's range 437 * 438 */ 439 public void moveRow(int start, int end, int to) { 440 int shift = to - start; 441 int first, last; 442 if (shift < 0) { 443 first = to; 444 last = end; 445 } 446 else { 447 first = start; 448 last = to + end - start; 449 } 450 rotate(dataVector, first, last + 1, shift); 451 452 fireTableRowsUpdated(first, last); 453 } 454 455 /** 456 * Removes the row at <code>row</code> from the model. Notification 457 * of the row being removed will be sent to all the listeners. 458 * 459 * @param row the row index of the row to be removed 460 * @exception ArrayIndexOutOfBoundsException if the row was invalid 461 */ 462 public void removeRow(int row) { 463 dataVector.removeElementAt(row); 464 fireTableRowsDeleted(row, row); 465 } 466 467 // 468 // Manipulating columns 469 // 470 471 /** 472 * Replaces the column identifiers in the model. If the number of 473 * <code>newIdentifier</code>s is greater than the current number 474 * of columns, new columns are added to the end of each row in the model. 475 * If the number of <code>newIdentifier</code>s is less than the current 476 * number of columns, all the extra columns at the end of a row are 477 * discarded. <p> 478 * 479 * @param columnIdentifiers vector of column identifiers. If 480 * <code>null</code>, set the model 481 * to zero columns 482 * @see #setNumRows 483 */ 484 public void setColumnIdentifiers(Vector columnIdentifiers) { 485 setDataVector(dataVector, columnIdentifiers); 486 } 487 488 /** 489 * Replaces the column identifiers in the model. If the number of 490 * <code>newIdentifier</code>s is greater than the current number 491 * of columns, new columns are added to the end of each row in the model. 492 * If the number of <code>newIdentifier</code>s is less than the current 493 * number of columns, all the extra columns at the end of a row are 494 * discarded. <p> 495 * 496 * @param newIdentifiers array of column identifiers. 497 * If <code>null</code>, set 498 * the model to zero columns 499 * @see #setNumRows 500 */ 501 public void setColumnIdentifiers(Object[] newIdentifiers) { 502 setColumnIdentifiers(convertToVector(newIdentifiers)); 503 } 504 505 /** 506 * Sets the number of columns in the model. If the new size is greater 507 * than the current size, new columns are added to the end of the model 508 * with <code>null</code> cell values. 509 * If the new size is less than the current size, all columns at index 510 * <code>columnCount</code> and greater are discarded. 511 * 512 * @param columnCount the new number of columns in the model 513 * 514 * @see #setColumnCount 515 * @since 1.3 516 */ 517 public void setColumnCount(int columnCount) { 518 columnIdentifiers.setSize(columnCount); 519 justifyRows(0, getRowCount()); 520 fireTableStructureChanged(); 521 } 522 523 /** 524 * Adds a column to the model. The new column will have the 525 * identifier <code>columnName</code>, which may be null. This method 526 * will send a 527 * <code>tableChanged</code> notification message to all the listeners. 528 * This method is a cover for <code>addColumn(Object, Vector)</code> which 529 * uses <code>null</code> as the data vector. 530 * 531 * @param columnName the identifier of the column being added 532 */ 533 public void addColumn(Object columnName) { 534 addColumn(columnName, (Vector)null); 535 } 536 537 /** 538 * Adds a column to the model. The new column will have the 539 * identifier <code>columnName</code>, which may be null. 540 * <code>columnData</code> is the 541 * optional vector of data for the column. If it is <code>null</code> 542 * the column is filled with <code>null</code> values. Otherwise, 543 * the new data will be added to model starting with the first 544 * element going to row 0, etc. This method will send a 545 * <code>tableChanged</code> notification message to all the listeners. 546 * 547 * @param columnName the identifier of the column being added 548 * @param columnData optional data of the column being added 549 */ 550 public void addColumn(Object columnName, Vector columnData) { 551 columnIdentifiers.addElement(columnName); 552 if (columnData != null) { 553 int columnSize = columnData.size(); 554 if (columnSize > getRowCount()) { 555 dataVector.setSize(columnSize); 556 } 557 justifyRows(0, getRowCount()); 558 int newColumn = getColumnCount() - 1; 559 for(int i = 0; i < columnSize; i++) { 560 Vector row = (Vector)dataVector.elementAt(i); 561 row.setElementAt(columnData.elementAt(i), newColumn); 562 } 563 } 564 else { 565 justifyRows(0, getRowCount()); 566 } 567 568 fireTableStructureChanged(); 569 } 570 571 /** 572 * Adds a column to the model. The new column will have the 573 * identifier <code>columnName</code>. <code>columnData</code> is the 574 * optional array of data for the column. If it is <code>null</code> 575 * the column is filled with <code>null</code> values. Otherwise, 576 * the new data will be added to model starting with the first 577 * element going to row 0, etc. This method will send a 578 * <code>tableChanged</code> notification message to all the listeners. 579 * 580 * @see #addColumn(Object, Vector) 581 */ 582 public void addColumn(Object columnName, Object[] columnData) { 583 addColumn(columnName, convertToVector(columnData)); 584 } 585 586 // 587 // Implementing the TableModel interface 588 // 589 590 /** 591 * Returns the number of rows in this data table. 592 * @return the number of rows in the model 593 */ 594 public int getRowCount() { 595 return dataVector.size(); 596 } 597 598 /** 599 * Returns the number of columns in this data table. 600 * @return the number of columns in the model 601 */ 602 public int getColumnCount() { 603 return columnIdentifiers.size(); 604 } 605 606 /** 607 * Returns the column name. 608 * 609 * @return a name for this column using the string value of the 610 * appropriate member in <code>columnIdentifiers</code>. 611 * If <code>columnIdentifiers</code> does not have an entry 612 * for this index, returns the default 613 * name provided by the superclass. 614 */ 615 public String getColumnName(int column) { 616 Object id = null; 617 // This test is to cover the case when 618 // getColumnCount has been subclassed by mistake ... 619 if (column < columnIdentifiers.size() && (column >= 0)) { 620 id = columnIdentifiers.elementAt(column); 621 } 622 return (id == null) ? super.getColumnName(column) 623 : id.toString(); 624 } 625 626 /** 627 * Returns true regardless of parameter values. 628 * 629 * @param row the row whose value is to be queried 630 * @param column the column whose value is to be queried 631 * @return true 632 * @see #setValueAt 633 */ 634 public boolean isCellEditable(int row, int column) { 635 return true; 636 } 637 638 /** 639 * Returns an attribute value for the cell at <code>row</code> 640 * and <code>column</code>. 641 * 642 * @param row the row whose value is to be queried 643 * @param column the column whose value is to be queried 644 * @return the value Object at the specified cell 645 * @exception ArrayIndexOutOfBoundsException if an invalid row or 646 * column was given 647 */ 648 public Object getValueAt(int row, int column) { 649 Vector rowVector = (Vector)dataVector.elementAt(row); 650 return rowVector.elementAt(column); 651 } 652 653 /** 654 * Sets the object value for the cell at <code>column</code> and 655 * <code>row</code>. <code>aValue</code> is the new value. This method 656 * will generate a <code>tableChanged</code> notification. 657 * 658 * @param aValue the new value; this can be null 659 * @param row the row whose value is to be changed 660 * @param column the column whose value is to be changed 661 * @exception ArrayIndexOutOfBoundsException if an invalid row or 662 * column was given 663 */ 664 public void setValueAt(Object aValue, int row, int column) { 665 Vector rowVector = (Vector)dataVector.elementAt(row); 666 rowVector.setElementAt(aValue, column); 667 fireTableCellUpdated(row, column); 668 } 669 670 // 671 // Protected Methods 672 // 673 674 /** 675 * Returns a vector that contains the same objects as the array. 676 * @param anArray the array to be converted 677 * @return the new vector; if <code>anArray</code> is <code>null</code>, 678 * returns <code>null</code> 679 */ 680 protected static Vector convertToVector(Object[] anArray) { 681 if (anArray == null) { 682 return null; 683 } 684 Vector<Object> v = new Vector<Object>(anArray.length); 685 for (Object o : anArray) { 686 v.addElement(o); 687 } 688 return v; 689 } 690 691 /** 692 * Returns a vector of vectors that contains the same objects as the array. 693 * @param anArray the double array to be converted 694 * @return the new vector of vectors; if <code>anArray</code> is 695 * <code>null</code>, returns <code>null</code> 696 */ 697 protected static Vector convertToVector(Object[][] anArray) { 698 if (anArray == null) { 699 return null; 700 } 701 Vector<Vector> v = new Vector<Vector>(anArray.length); 702 for (Object[] o : anArray) { 703 v.addElement(convertToVector(o)); 704 } 705 return v; 706 } 707 708 } // End of class DefaultTableModel